#include "printers.h"
#include "file_cmd.h"
#include "int_util.h"
#include <stdio.h>

/****************************************************************************\
 * Private Variables/Functions
\****************************************************************************/

static void s_print_comimage_flags(u4 flag)
{
	if(flag & COMIMAGE_FLAGS_ILONLY)
		wprintf(L"    (%08Xh) COMIMAGE_FLAGS_ILONLY\n", COMIMAGE_FLAGS_ILONLY);

	if(flag & COMIMAGE_FLAGS_32BITREQUIRED)
		wprintf(L"    (%08Xh) COMIMAGE_FLAGS_32BITREQUIRED\n", COMIMAGE_FLAGS_32BITREQUIRED);

	if(flag & COMIMAGE_FLAGS_IL_LIBRARY)
		wprintf(L"    (%08Xh) COMIMAGE_FLAGS_IL_LIBRARY\n", COMIMAGE_FLAGS_IL_LIBRARY);

	if(flag & COMIMAGE_FLAGS_STRONGNAMESIGNED)
		wprintf(L"    (%08Xh) COMIMAGE_FLAGS_STRONGNAMESIGNED\n", COMIMAGE_FLAGS_STRONGNAMESIGNED);

	if(flag & COMIMAGE_FLAGS_NATIVE_ENTRYPOINT)
		wprintf(L"    (%08Xh) COMIMAGE_FLAGS_NATIVE_ENTRYPOINT\n", COMIMAGE_FLAGS_NATIVE_ENTRYPOINT);

	if(flag & COMIMAGE_FLAGS_TRACKDEBUGDATA)
		wprintf(L"    (%08Xh) COMIMAGE_FLAGS_TRACKDEBUGDATA\n", COMIMAGE_FLAGS_TRACKDEBUGDATA);

	if(flag & COMIMAGE_FLAGS_ISIBCOPTIMIZED)
		wprintf(L"    (%08Xh) COMIMAGE_FLAGS_ISIBCOPTIMIZED\n", COMIMAGE_FLAGS_ISIBCOPTIMIZED);
}

static void s_print_md_stream_offset(pe_file *p_pe, u4 md_offset)
{
	size_t rva = p_pe->metadata.p_cli_hdr->MetaData.VirtualAddress + md_offset;
	size_t offset = rva_to_offset(p_pe, rva);

	wprintf(L"(RVA=%08Xh, FileOffset=%08Xh)", rva, offset);
}

static void s_print_guid(guid *p_guid)
{
	wprintf(L"{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}",
		p_guid->part1, p_guid->part2, p_guid->part3,
		p_guid->part4[0], p_guid->part4[1], p_guid->part4[2], p_guid->part4[3],
		p_guid->part4[4], p_guid->part4[5], p_guid->part4[6], p_guid->part4[7]);
}

static void s_print_bytes(byte *bytes, size_t n)
{
	size_t i;

	if(n > 0)
		wprintf(L"     %02X", bytes[0]);

	for(i = 1; i < n; i++)
	{
		if(i % 16)
			wprintf(L"-%02X", bytes[i]);
		else
			wprintf(L"\n    -%02X", bytes[i]);
	}
}

void s_print_md_res(pe_file *p_pe, size_t rid)
{
}

/****************************************************************************\
 * Public Functions
\****************************************************************************/

void print_cli_header(pe_file *p_pe)
{
	byte *pb = p_pe->p_base;
	image_cor20_header *ph = p_pe->metadata.p_cli_hdr;
	wchar_t dd_str_buf[DATA_DIRECTORY_STR_LEN];

	if(!check_file_opened() || !check_pe_headers(p_pe) || !check_metadata(p_pe))
		return;

	wprintf(L"IMAGE_COR20_HEADER:\n");

	wprintf(L"%08Xh ", PTR_D(pb, &ph->cb));
	wprintf(L"%01Xh ", sizeof(ph->cb));
	wprintf(L"%24s ", L"cb");
	wprintf(L"%08Xh (%d)\n", ph->cb, ph->cb);

	wprintf(L"%08Xh ", PTR_D(pb, &ph->MajorRuntimeVersion));
	wprintf(L"%01Xh ", sizeof(ph->MajorRuntimeVersion));
	wprintf(L"%24s ", L"MajorRuntimeVersion");
	wprintf(L"%04Xh     (%d)\n", ph->MajorRuntimeVersion, ph->MajorRuntimeVersion);

	wprintf(L"%08Xh ", PTR_D(pb, &ph->MinorRuntimeVersion));
	wprintf(L"%01Xh ", sizeof(ph->MinorRuntimeVersion));
	wprintf(L"%24s ", L"MinorRuntimeVersion");
	wprintf(L"%04Xh     (%d)\n", ph->MinorRuntimeVersion, ph->MinorRuntimeVersion);

	wmemchr(dd_str_buf, L'\0', DATA_DIRECTORY_STR_LEN);
	format_data_dir(p_pe, &ph->MetaData, dd_str_buf, DATA_DIRECTORY_STR_LEN - 1);
	wprintf(L"%08Xh ", PTR_D(pb, &ph->MetaData));
	wprintf(L"%01Xh ", sizeof(ph->MetaData));
	wprintf(L"%24s %s\n", L"MetaData", dd_str_buf);

	wprintf(L"%08Xh ", PTR_D(pb, &ph->Flags));
	wprintf(L"%01Xh ", sizeof(ph->Flags));
	wprintf(L"%24s ", L"Flags");
	wprintf(L"%08Xh\n", ph->Flags);
	s_print_comimage_flags(ph->Flags);

	wprintf(L"%08Xh ", PTR_D(pb, &ph->EntryPointRVA));
	wprintf(L"%01Xh ", sizeof(ph->EntryPointRVA));
	wprintf(L"%24s ", (ph->Flags & COMIMAGE_FLAGS_NATIVE_ENTRYPOINT) ? L"EntryPointRVA" : L"EntryPointToken");
	wprintf(L"%08Xh ", ph->EntryPointRVA);

	if(ph->EntryPointToken)
	{
		if(ph->Flags & COMIMAGE_FLAGS_NATIVE_ENTRYPOINT)
		{
			wchar_t rva_str[RVA_STR_LEN + 1];
			format_rva_str(p_pe, ph->EntryPointRVA, rva_str, RVA_STR_LEN);
			wprintf(L"%s\n", rva_str);
		}
		else
		{
			wchar_t mdt_str[METADATA_TOKEN_STR_LEN + 1];
			format_metadata_token(p_pe, ph->EntryPointToken, mdt_str, METADATA_TOKEN_STR_LEN);
			wprintf(L"%s\n", mdt_str);
		}
	}
	else
		wprintf(L"(None)\n");

	wprintf(L"\n");

	wmemchr(dd_str_buf, L'\0', DATA_DIRECTORY_STR_LEN);
	format_data_dir(p_pe, &ph->Resources, dd_str_buf, DATA_DIRECTORY_STR_LEN - 1);
	wprintf(L"%08Xh ", PTR_D(pb, &ph->Resources));
	wprintf(L"%01Xh ", sizeof(ph->Resources));
	wprintf(L"%24s %s\n", L"Resources", dd_str_buf);

	wmemchr(dd_str_buf, L'\0', DATA_DIRECTORY_STR_LEN);
	format_data_dir(p_pe, &ph->StrongNameSignature, dd_str_buf, DATA_DIRECTORY_STR_LEN - 1);
	wprintf(L"%08Xh ", PTR_D(pb, &ph->StrongNameSignature));
	wprintf(L"%01Xh ", sizeof(ph->StrongNameSignature));
	wprintf(L"%24s %s\n", L"StrongNameSignature", dd_str_buf);

	wmemchr(dd_str_buf, L'\0', DATA_DIRECTORY_STR_LEN);
	format_data_dir(p_pe, &ph->CodeManagerTable, dd_str_buf, DATA_DIRECTORY_STR_LEN - 1);
	wprintf(L"%08Xh ", PTR_D(pb, &ph->CodeManagerTable));
	wprintf(L"%01Xh ", sizeof(ph->CodeManagerTable));
	wprintf(L"%24s %s\n", L"CodeManagerTable", dd_str_buf);

	wmemchr(dd_str_buf, L'\0', DATA_DIRECTORY_STR_LEN);
	format_data_dir(p_pe, &ph->VTableFixups, dd_str_buf, DATA_DIRECTORY_STR_LEN - 1);
	wprintf(L"%08Xh ", PTR_D(pb, &ph->VTableFixups));
	wprintf(L"%01Xh ", sizeof(ph->VTableFixups));
	wprintf(L"%24s %s\n", L"VTableFixups", dd_str_buf);

	wmemchr(dd_str_buf, L'\0', DATA_DIRECTORY_STR_LEN);
	format_data_dir(p_pe, &ph->ExportAddressTableJumps, dd_str_buf, DATA_DIRECTORY_STR_LEN - 1);
	wprintf(L"%08Xh ", PTR_D(pb, &ph->ExportAddressTableJumps));
	wprintf(L"%01Xh ", sizeof(ph->ExportAddressTableJumps));
	wprintf(L"%24s %s\n", L"ExportAddressTableJumps", dd_str_buf);

	wmemchr(dd_str_buf, L'\0', DATA_DIRECTORY_STR_LEN);
	format_data_dir(p_pe, &ph->ManagedNativeHeader, dd_str_buf, DATA_DIRECTORY_STR_LEN - 1);
	wprintf(L"%08Xh ", PTR_D(pb, &ph->ManagedNativeHeader));
	wprintf(L"%01Xh ", sizeof(ph->ManagedNativeHeader));
	wprintf(L"%24s %s\n", L"ManagedNativeHeader", dd_str_buf);
}

void print_md_storage_sig(pe_file *p_pe)
{
	byte *pb = p_pe->p_base;
	metadata_storage_signature *ph = p_pe->metadata.p_st_sig;

	if(!check_file_opened() || !check_pe_headers(p_pe) || !check_metadata(p_pe))
		return;

	wprintf(L"Metadata Storage Signature:\n");

	wprintf(L"%08Xh ", PTR_D(pb, &ph->Signature));
	wprintf(L"%01Xh ", sizeof(ph->Signature));
	wprintf(L"%14s ", L"Signature");
	wprintf(L"%08Xh\n", ph->Signature);

	wprintf(L"%08Xh ", PTR_D(pb, &ph->MajorVersion));
	wprintf(L"%01Xh ", sizeof(ph->MajorVersion));
	wprintf(L"%14s ", L"MajorVersion");
	wprintf(L"%04Xh     (%d)\n", ph->MajorVersion, ph->MajorVersion);

	wprintf(L"%08Xh ", PTR_D(pb, &ph->MinorVersion));
	wprintf(L"%01Xh ", sizeof(ph->MinorVersion));
	wprintf(L"%14s ", L"MinorVersion");
	wprintf(L"%04Xh     (%d)\n", ph->MinorVersion, ph->MinorVersion);

	wprintf(L"%08Xh ", PTR_D(pb, &ph->Reserved));
	wprintf(L"%01Xh ", sizeof(ph->Reserved));
	wprintf(L"%14s ", L"Reserved");
	wprintf(L"%08Xh\n", ph->Reserved);

	wprintf(L"%08Xh ", PTR_D(pb, &ph->VersionLength));
	wprintf(L"%01Xh ", sizeof(ph->VersionLength));
	wprintf(L"%14s ", L"VersionLength");
	wprintf(L"%08Xh (%d)\n", ph->VersionLength, ph->VersionLength);

	wprintf(L"%08Xh ", PTR_D(pb, ph->VersionString));
	wprintf(L"%01Xh ", ph->VersionLength);
	wprintf(L"%14s ", L"VersionString");
	{
		u4 i;

		for(i = 0; i < ph->VersionLength; ++i)
			wprintf(L"%02X ", ph->VersionString[i]);

		wprintf(L"    ");

		for(i = 0; i < ph->VersionLength; ++i)
			wprintf(L"%c", ph->VersionString[i]);
	}
	wprintf(L"\n");
}

void print_md_storage_hdr(pe_file *p_pe)
{
	byte *pb = p_pe->p_base;
	metadata_storage_header *ph = p_pe->metadata.p_st_hdr;

	if(!check_file_opened() || !check_pe_headers(p_pe) || !check_metadata(p_pe))
		return;

	wprintf(L"Metadata Storage Header:\n");

	wprintf(L"%08Xh ", PTR_D(pb, &ph->Flags));
	wprintf(L"%01Xh ", sizeof(ph->Flags));
	wprintf(L"%16s ", L"Flags");
	wprintf(L"%04Xh\n", ph->Flags);

	wprintf(L"%08Xh ", PTR_D(pb, &ph->NumberOfStreams));
	wprintf(L"%01Xh ", sizeof(ph->NumberOfStreams));
	wprintf(L"%16s ", L"NumberOfStreams");
	wprintf(L"%04Xh (%d)\n", ph->NumberOfStreams, ph->NumberOfStreams);
}

void print_md_stream_hdrs(pe_file *p_pe)
{
	byte *pb = p_pe->p_base;
	metadata_stream_header *ph = p_pe->metadata.p_stream_hdr;
	int i = 0;
	size_t j;
	size_t name_len = 0;

	if(!check_file_opened() || !check_pe_headers(p_pe) || !check_metadata(p_pe))
		return;

	while(i < p_pe->metadata.p_st_hdr->NumberOfStreams)
	{
		wprintf(L"Metadata Stream Header[%d]:\n", i);

		wprintf(L"%08Xh ", PTR_D(pb, &ph->Offset));
		wprintf(L"%01Xh ", sizeof(ph->Offset));
		wprintf(L"%7s ", L"Offset");
		wprintf(L"%08Xh ", ph->Offset);
		s_print_md_stream_offset(p_pe, ph->Offset);
		wprintf(L"\n");

		wprintf(L"%08Xh ", PTR_D(pb, &ph->Size));
		wprintf(L"%01Xh ", sizeof(ph->Size));
		wprintf(L"%7s ", L"Size");
		wprintf(L"%08Xh (%d)\n", ph->Size, ph->Size);

		for(name_len = 0; ph->Name[name_len]; ++name_len);
		wprintf(L"%08Xh ", PTR_D(pb, ph->Name));
		wprintf(L"%01Xh ", name_len);
		wprintf(L"%7s ", L"Name");
		{
			for(j = 0; j < name_len; ++j)
				wprintf(L"%02X ", ph->Name[j]);

			wprintf(L"(");
			for(j = 0; j < name_len; ++j)
				wprintf(L"%c", ph->Name[j]);
			wprintf(L")\n");
		}

		if(i < p_pe->metadata.p_st_hdr->NumberOfStreams - 1)
			wprintf(L"\n");

		ph = next_stream_header(ph);
		++i;
	}
}

void print_md_headers(pe_file *p_pe)
{
	if(!check_file_opened() || !check_pe_headers(p_pe) || !check_metadata(p_pe))
		return;

	print_md_storage_sig(p_pe);
	wprintf(L"\n");
	print_md_storage_hdr(p_pe);
	wprintf(L"\n");
	print_md_stream_hdrs(p_pe);
}

void print_md_strings(pe_file *p_pe, size_t n)
{
	size_t offset, item_len;
	const char *p_char;

	if(!check_file_opened() || !check_pe_headers(p_pe) || !check_metadata(p_pe))
		return;

	if(n >= p_pe->metadata.strings_length)
	{
		wprintf(L"Out of range.\n");
		return;
	}

	wprintf(L"#Strings[%Xh]\n", n);

	offset = p_pe->metadata.strings_offset + n;
	p_char = (const char *)(p_pe->p_base + offset);
	for(item_len = 0; p_char[item_len]; ++item_len);
	++item_len;  // Include the terminator '\0'
	wprintf(L"%08Xh : %Xh (%d)\n", offset, item_len, item_len);
	printf(p_char);
	wprintf(L"\n");
}

void print_md_us(pe_file *p_pe, size_t n)
{
	size_t offset, item_sz_len;
	u4 item_val_len;
	byte *p_data;
	const wchar_t *str_val;

	if(!check_file_opened() || !check_pe_headers(p_pe) || !check_metadata(p_pe))
		return;

	if(n >= p_pe->metadata.us_length)
	{
		wprintf(L"Out of range.\n");
		return;
	}

	wprintf(L"#US[%Xh]\n", n);

	offset = p_pe->metadata.us_offset + n;
	p_data = p_pe->p_base + offset;

	if(!decode_u4(p_data, &item_sz_len, &item_val_len))
	{
		wprintf(L"Encount invalid compressed unsigned integer at %08Xh.\n", p_data);
		return;
	}

	wprintf(L"%08Xh %8Xh Size\n", PTR_D(p_pe->p_base, p_data), item_sz_len);
	s_print_bytes(p_data, item_sz_len);
	wprintf(L" (%Xh, %d)\n", item_val_len, item_val_len);

	if(item_val_len > 0)
	{
		p_data += item_sz_len;
		str_val = (const wchar_t *)p_data;
		wprintf(L"%08Xh %8Xh Data\n", PTR_D(p_pe->p_base, p_data), item_val_len - 1);
		s_print_bytes(p_data, item_val_len - 1);
		wprintf(L"\n");

		p_data += (item_val_len - 1);
		wprintf(L"%08Xh %8Xh LastByte\n", PTR_D(p_pe->p_base, p_data), 1);
		s_print_bytes(p_data, 1);
		wprintf(L" (%s)\n", *p_data ? L"Contains extended chars." : L"No extended chars.");

		wprintf(L"String value:\n");
		wprintf(str_val);  // Note, cannot embedded into one single wprintf call.
		wprintf(L"\n");
	}
}

void print_md_guid(pe_file *p_pe, size_t n)
{
	size_t offset, item_len;
	guid *p_guid;

	if(!check_file_opened() || !check_pe_headers(p_pe) || !check_metadata(p_pe))
		return;

	offset = (n - 1) * 16;
	if(offset >= p_pe->metadata.guid_length)
	{
		wprintf(L"Out of range.\n");
		return;
	}

	wprintf(L"#GUID[%Xh]:\n", n);

	offset += p_pe->metadata.guid_offset;
	item_len = 16;
	wprintf(L"%08Xh : %Xh (%d)\n", offset, item_len, item_len);
	p_guid = (guid *)(p_pe->p_base + offset);
	s_print_guid(p_guid);
	wprintf(L"\n");
}

void print_md_blob(pe_file *p_pe, size_t n)
{
	size_t offset, item_sz_len;
	u4 item_val_len;
	byte *p_data;

	if(!check_file_opened() || !check_pe_headers(p_pe) || !check_metadata(p_pe))
		return;

	if(n >= p_pe->metadata.blob_length)
	{
		wprintf(L"Out of range.\n");
		return;
	}

	wprintf(L"#Blob[%Xh]\n", n);

	offset = p_pe->metadata.blob_offset + n;
	p_data = p_pe->p_base + offset;

	if(!decode_u4(p_data, &item_sz_len, &item_val_len))
	{
		wprintf(L"Encount invalid compressed unsigned integer at %08Xh.\n", p_data);
		return;
	}

	wprintf(L"%08Xh %8Xh Size\n", PTR_D(p_pe->p_base, p_data), item_sz_len);
	s_print_bytes(p_data, item_sz_len);
	wprintf(L" (%Xh, %d)\n", item_val_len, item_val_len);

	p_data += item_sz_len;
	wprintf(L"%08Xh %8Xh Data\n", PTR_D(p_pe->p_base, p_data), item_val_len);
	s_print_bytes(p_data, item_val_len);
	wprintf(L"\n");
}

void print_md_sn_signature(pe_file *p_pe)
{
	size_t sn_off;
	image_data_directory dd;
	wchar_t dd_str[DATA_DIRECTORY_STR_LEN + 1];

	if(!check_file_opened() || !check_pe_headers(p_pe) || !check_metadata(p_pe))
		return;

	dd = p_pe->metadata.p_cli_hdr->StrongNameSignature;
	if(!dd.VirtualAddress)
	{
		wprintf(L"This file has no strong name signature.\n");
		return;
	}

	sn_off = rva_to_offset(p_pe, dd.VirtualAddress);

	wprintf(L"Strong name signature:\n");
	format_data_dir(p_pe, &dd, dd_str, DATA_DIRECTORY_STR_LEN);
	wprintf(dd_str);
	wprintf(L"\n\n");
	exec_dump_data(sn_off, dd.Size);
}

void print_md_res(pe_file *p_pe, size_t start, size_t len)
{
	size_t rid, res_offset;
	image_data_directory dd;
	mdt_tbl_def *p_tbl;
	byte *p_row, *p_res;
	mdt_col_def *p_col_offset, *p_col_name, *p_col_impl;
	u4 offset_value, name_value, impl_value, res_size;

	if(!check_file_opened() || !check_pe_headers(p_pe) || !check_metadata(p_pe))
		return;

	dd = p_pe->metadata.p_cli_hdr->Resources;
	p_tbl = p_pe->metadata.tbl_defs + TBLID_ManifestResource;

	if(!dd.VirtualAddress || !p_tbl->record_num)
	{
		wprintf(L"This file has no managed resources.\n");
		return;
	}

	if(start < 1 || start > p_tbl->record_num || start + len > p_tbl->record_num + 1)
	{
		wprintf(L"Invalid range (valid value should be 1-%d).\n",
			p_tbl->record_num);
		return;
	}

	p_col_offset = p_pe->metadata.col_defs + p_tbl->cols;
	p_col_name   = p_pe->metadata.col_defs + p_tbl->cols + 2;
	p_col_impl   = p_pe->metadata.col_defs + p_tbl->cols + 3;
	res_offset = rva_to_offset(p_pe, dd.VirtualAddress);

	for(rid = start; rid < start + len; ++rid)
	{
		if(rid > start)
			wprintf(L"\n");

		wprintf(L"Manifest Resource[%d] (", rid);
		p_row = p_tbl->p_data + p_tbl->record_size * (rid - 1);
		offset_value = p_col_offset->size == 2
			? *((u2 *)(p_row + p_col_offset->offset))
			: *((u4 *)(p_row + p_col_offset->offset));
		name_value = p_col_name->size == 2
			? *((u2 *)(p_row + p_col_name->offset))
			: *((u4 *)(p_row + p_col_name->offset));
		impl_value = p_col_impl->size == 2
			? *((u2 *)(p_row + p_col_impl->offset))
			: *((u4 *)(p_row + p_col_impl->offset));

		printf((const char *)(p_pe->p_base + p_pe->metadata.strings_offset + name_value));
		wprintf(L"):\n");
		if(impl_value)
		{
			wprintf(L"This resource is implemented in other file.\n");
		}
		else
		{
			p_res = p_pe->p_base + res_offset + offset_value;
			res_size = *((u4 *)p_res);
			wprintf(L"%08Xh 00000004h Size %08Xh (%d)\n", PTR_D(p_pe->p_base, p_res), res_size, res_size);
			p_res += 4;
			wprintf(L"%08Xh %08Xh Data\n", PTR_D(p_pe->p_base, p_res), res_size);
		}
	}
}

void print_md_vtfixup(pe_file *p_pe, size_t start, size_t len)
{
	size_t n_fixup, i_vtf, j_vts;
	image_data_directory *p_ddvtf;
	image_cor_vtablefixup *p_vtf;
	wchar_t rva_str[RVA_STR_LEN];

	if(!check_file_opened() || !check_pe_headers(p_pe) || !check_metadata(p_pe))
		return;

	p_ddvtf = &p_pe->metadata.p_cli_hdr->VTableFixups;

	if(!p_ddvtf->VirtualAddress)
	{
		wprintf(L"This file contains no V-Table Fixups.\n");
		return;
	}

	n_fixup = p_ddvtf->Size / sizeof(image_cor_vtablefixup);

	wprintf(L"V-Table Fixups (%d records)\n\n", n_fixup);

	if(start >= n_fixup || start + len > n_fixup)
	{
		wprintf(L"Index out of range.\n");
		return;
	}

	p_vtf = (image_cor_vtablefixup *)(p_pe->p_base
		+ rva_to_offset(p_pe, p_ddvtf->VirtualAddress)) + start;
	for(i_vtf = start; i_vtf < start + len; ++i_vtf, ++p_vtf)
	{
		if(i_vtf > start)
			wprintf(L"\n");

		wprintf(L"[%d] IMAGE_COR_VTABLEFIXUP:\n", i_vtf);
		wprintf(L"%08Xh %Xh %5s %08X",
			PTR_D(p_pe->p_base, &p_vtf->RVA),
			sizeof(p_vtf->RVA),
			L"RVA",
			p_vtf->RVA);
		format_rva_str(p_pe, p_vtf->RVA, rva_str, RVA_STR_LEN);
		wprintf(rva_str);
		wprintf(L"\n");

		wprintf(L"%08Xh %Xh %5s %04X\n",
			PTR_D(p_pe->p_base, &p_vtf->Count),
			sizeof(p_vtf->Count),
			L"Count",
			p_vtf->Count);

		wprintf(L"%08Xh %Xh %5s %04X",
			PTR_D(p_pe->p_base, &p_vtf->Type),
			sizeof(p_vtf->Type),
			L"Type",
			p_vtf->Type);

		if(p_vtf->Type & COR_VTABLE_32BIT)
			wprintf(L" COR_VTABLE_32BIT");
		if(p_vtf->Type & COR_VTABLE_64BIT)
			wprintf(L" COR_VTABLE_64BIT");
		if(p_vtf->Type & COR_VTABLE_FROM_UNMANAGED)
			wprintf(L" COR_VTABLE_FROM_UNMANAGED");
		if(p_vtf->Type & COR_VTABLE_FROM_UNMANAGED_RETAIN_APPDOMAIN)
			wprintf(L" COR_VTABLE_FROM_UNMANAGED_RETAIN_APPDOMAIN");
		if(p_vtf->Type & COR_VTABLE_CALL_MOST_DERIVED)
			wprintf(L" COR_VTABLE_CALL_MOST_DERIVED");
		wprintf(L"\n\n");

		for(j_vts = 0; j_vts < p_vtf->Count; ++j_vts)
		{
			size_t offset = rva_to_offset(p_pe, p_vtf->RVA);
			u4 *p_data = (u4 *)(p_pe->p_base + offset);
			wprintf(L"%08X\n", *p_data);
		}
	}
}
